﻿using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Threading;
using System.Text;

namespace NW
{
	public delegate void NWServerEvent(NWServer sender);
	public delegate void NWServerClientEvent(NWServer sender, NWClient subject);

	public class NWServer
	{
		TcpListener Listener;

		public List<NWClient> Clients = new List<NWClient>();
		public event NWServerEvent Stopped;
		public event NWServerEvent StopForce;
		public event NWServerEvent StopPrevented;

		public event NWServerClientEvent ClientConnected;
		public event NWServerClientEvent ClientLeft;
		public NWServer(int port)
		{
			Listener = new TcpListener(port);
		}
		bool Listening;
		void Core()
		{
			Listener.Start();
			while (Listening)
			{
				try
				{
					TcpClient newclient = Listener.AcceptTcpClient();
					NWClient C = MakeClient(newclient);
					C.Disconnected += SCExited;
					C.EchoReceived += sender => ClientEchoRcv(this, sender);
					C.EchoReplied += sender => ClientEchoReply(this, sender);
					Clients.Add(C);
					C.Start();
					ClientConnected(this, C);
				}
				catch (SocketException e)
				{
					if (e.ErrorCode == 10004)
						Listening = false;
					else
						throw new Exception("Unhandled error with server socket");
				}
				catch (Exception e)
				{
					Console.WriteLine("Listener stopped: " + e.Message+"\n"+e.StackTrace);
				}
			}
			if (Stopped != null)
				Stopped(this);
		}
		public event NWServerClientEvent ClientEchoRcv;
		public event NWServerClientEvent ClientEchoReply;
		public event NWServerClientEvent ClientExit;

		public event NWServerEvent ClientListEmpty;

		void SCExited(NWClient sender)
		{
			Clients.Remove(sender);
			//if (ClientExit != null)
			ClientExit(this, sender);
			if (Clients.Count == 0)
			{
				if (Stopping)
				{
					Console.WriteLine("All clients exited");
					CloseListener();
				}
				else
					if (ClientListEmpty != null)
						ClientListEmpty(this);
			}
		}
		protected virtual NWClient MakeClient(TcpClient C)
		{
			return new NWClient(C);
		}

		public void Start()
		{
			Listening = true;
			Thread T = new Thread(Core);
			T.Name = "Server core";
			T.Start();
		}

		bool Stopping = false;
		public void Stop(bool ForceClients)
		{
			Stopping = true;
			if (ForceClients)
			{
				if (StopForce != null)
					StopForce(this);
				foreach (NWClient C in Clients)
					C.Disconnect();
			}
			else if (Clients.Count > 0)
			{
				Stopping = false;
				if (StopPrevented != null)
					StopPrevented(this);
			}
			if (Stopping)
			{
				CloseListener();
			}
		}
		void CloseListener()
		{
			Listening = false;
			Listener.Stop();
		}
	}
}